package com.lfy.modules.blog.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lfy.modules.common.config.sys.DictionariesKey;
import com.lfy.modules.common.config.sys.SysDictionaries;
import com.lfy.modules.blog.domain.ArticleComment;
import com.lfy.modules.blog.domain.ArticleEntity;
import com.lfy.modules.blog.domain.ArticleTagEntity;
import com.lfy.modules.blog.domain.ArticleTextEntity;
import com.lfy.modules.blog.domain.dto.article.ArticleDTO;
import com.lfy.modules.blog.domain.dto.article.CommentDTO;
import com.lfy.modules.blog.domain.dto.article.ModifyArticleDTO;
import com.lfy.modules.blog.domain.event.ClearWebArticleDetailEvent;
import com.lfy.modules.blog.domain.vo.article.ArticleTitleAndIdVO;
import com.lfy.modules.blog.domain.vo.article.ArticleVO;
import com.lfy.modules.blog.mapper.ArticleCommentMapper;
import com.lfy.modules.blog.service.ArticleService;
import com.lfy.modules.blog.mapper.BlogArticleMapper;
import com.lfy.modules.blog.service.ArticleTagService;
import com.lfy.modules.blog.service.ArticleTextService;
import com.lfy.modules.common.custom.FileService;
import com.lfy.modules.common.custom.constants.ArticleEnum;
import com.lfy.modules.common.custom.constants.CommentEnum;
import com.lfy.modules.common.custom.constants.FileConstant;
import com.lfy.modules.common.domain.PageResultVO;
import com.lfy.modules.common.domain.R;
import com.lfy.modules.common.domain.entity.UserEntity;
import com.lfy.modules.common.intercept.AuthHandlerInterceptor;
import com.lfy.modules.common.utils.PageUtil;
import com.lfy.modules.common.utils.RequestUtil;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* @author 吕飞扬
* @description 针对表【blog_article】的数据库操作Service实现
* @createDate 2024-05-02 16:28:58
*/
@Service
public class ArticleServiceImpl extends ServiceImpl<BlogArticleMapper, ArticleEntity>
    implements ArticleService {
    @Resource
    private FileService fileService;
    @Resource
    private ArticleTextService articleTextService;
    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private ThreadPoolExecutor mainThreadPool;
    @Resource
    private ArticleTagService tagService;
    @Resource
    private ArticleCommentMapper commentMapper;


    @Override
    public R<Void> saveArticle(ArticleDTO dto, UserEntity user) {
        MultipartFile imgFile = dto.getImgFile();
        String coverDir = FileConstant.builderCoverDir(user.getId());
        String url = fileService.uploadSingle(imgFile, coverDir);
        ArticleEntity articleEntity = new ArticleEntity();

        articleEntity.setArticleImages( url);
        articleEntity.setArticleContent(dto.getContent());
        articleEntity.setClassification(dto.getCategory());

        articleEntity.setTitle(dto.getTitle());
        articleEntity.setRecommend(dto.getRecommend());
        articleEntity.setStatus(dto.getStatus());
        articleEntity.setTagId(dto.getTags());
        articleEntity.setTenantId(user.getId());
        articleEntity.setGoodsNum(0L);
        articleEntity.setShareNum(0L);
        articleEntity.setReadNum(0L);
        ArticleTextEntity articleTextEntity = new ArticleTextEntity();
        String prefix = SysDictionaries.getValue(DictionariesKey.SYS_MINIO_SITE);
        String zwf = SysDictionaries.getValue(DictionariesKey.IMG_ZWF);
        articleTextEntity.setArticleFullText(dto.getAllContent().replace(prefix,zwf));
        articleTextEntity.setTenantId(user.getId());
        articleTextEntity.setStatus(dto.getStatus());
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        boolean flag = false;
        try {
            this.save(articleEntity);
            articleTextEntity.setArticleId(articleEntity.getId());
            articleTextService.save(articleTextEntity);
            transactionManager.commit(status);
        }catch (Exception e) {
            transactionManager.rollback(status);
            flag = true;
        }
        if (flag) {
            mainThreadPool.execute(() -> {
               fileService.removeFileByNames(Arrays.asList(url));
            });
        }
        if(flag){
            return R.error("网络错误");
        }
        return R.success();
    }

    @Override
    public R<List<String>> uploadImg(UserEntity user, MultipartFile[] files) {
        String dir = FileConstant.builderArticleImgDir(user.getId());
        List<String> list = fileService.upload(files, dir);
        String prefix = SysDictionaries.getValue(DictionariesKey.SYS_MINIO_SITE);
        List<String> result = list.stream().map(prefix::concat).collect(Collectors.toList());
        return R.success(result);
    }

    @Override
    public R<PageResultVO<ArticleVO>> getArticleList(Integer pageNum, Integer pageSize) {
        UserEntity user = AuthHandlerInterceptor.authLocal.get();
        Page<ArticleEntity> page = new Page<>(pageNum, pageSize);
        IPage<ArticleEntity> pageResult = this.baseMapper.selectPage(page, new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getTenantId, user.getId()).orderByAsc(ArticleEntity::getSortNum));
        List<ArticleEntity> list = pageResult.getRecords();
        List<ArticleTagEntity> tagEntities = tagService.list(new LambdaQueryWrapper<ArticleTagEntity>().eq(ArticleTagEntity::getTenantId, user.getId()));
        List<ArticleTextEntity> textEntities = articleTextService.list(new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getTenantId, user.getId()));
        String prefix = SysDictionaries.getValue(DictionariesKey.SYS_MINIO_SITE);
        List<ArticleVO> result = list.stream().map(item -> {
            ArticleVO vo = new ArticleVO();
            BeanUtils.copyProperties(item, vo);
            vo.setCoverImg(prefix+item.getArticleImages());
            vo.setCategory(item.getClassification());
            vo.setCreateTime(DateUtil.formatDateTime(item.getCreateTime()));
            vo.setTagIds(Arrays.stream(item.getTagId().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
            String tagId = item.getTagId();
            String[] tagArr = tagId.split(",");
            ArrayList<String> tagList = new ArrayList<>();
            for (String s : tagArr) {
                for (ArticleTagEntity tagEntity : tagEntities) {
                    if (tagEntity.getId().equals(Long.parseLong(s))) {
                        tagList.add(tagEntity.getTagName());
                    }
                }
            }
            vo.setTags(tagList);
            if(!CollectionUtils.isEmpty(textEntities)){
                for (ArticleTextEntity textEntity : textEntities) {
                    if (textEntity.getArticleId().equals(item.getId())) {

                        String zwf = SysDictionaries.getValue(DictionariesKey.IMG_ZWF);
                        vo.setAllContent(textEntity.getArticleFullText().replace(zwf,prefix));
                    }
                }
            }

            return vo;
        }).collect(Collectors.toList());
        PageResultVO<ArticleVO> articleVOPageResultVO = PageUtil.buildPageResultVO(pageResult, result);
        return R.success(articleVOPageResultVO);
    }

    @Override
    public R<Void> modifyRecommend(Long articleId, Integer recommend) {
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setRecommend(recommend);
        this.baseMapper.update(articleEntity, new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        return R.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> upArticle(Long articleId) {
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setStatus(ArticleEnum.UP.getCode());
        ArticleTextEntity textEntity = new ArticleTextEntity();
        textEntity.setStatus(ArticleEnum.UP.getCode());
        this.baseMapper.update(articleEntity,new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        this.articleTextService.update(textEntity, new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getArticleId, articleId).eq(ArticleTextEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        return R.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> downArticle(Long articleId) {
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setStatus(ArticleEnum.DOWN.getCode());
        ArticleTextEntity textEntity = new ArticleTextEntity();
        textEntity.setStatus(ArticleEnum.DOWN.getCode());
        this.articleTextService.update(textEntity,new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getArticleId, articleId).eq(ArticleTextEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        this.baseMapper.update(articleEntity,new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        return R.success();
    }

    @Override
    public R<Void> deleteArticle(Long articleId) {
        ArticleEntity articleEntity = this.baseMapper.selectOne(new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        if (articleEntity == null) {
            return R.error("文章不存在");
        }
        String images = articleEntity.getArticleImages();
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        boolean flag = false;
        try {
            this.baseMapper.delete(new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
            articleTextService.remove(new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getArticleId, articleId).eq(ArticleTextEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
            commentMapper.delete(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getArticleId,articleId));
            transactionManager.commit(status);
        }catch (Exception e){
            transactionManager.rollback(status);
            flag = true;

        }
        if (flag) {
            return R.error("网络错误");
        }

        return R.success();
    }

    @Override
    public R<Void> modifyArticle(ModifyArticleDTO dto, UserEntity user) {
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setId(dto.getId());
        articleEntity.setTitle(dto.getTitle());
        articleEntity.setArticleContent(dto.getContent());
        List<Long> tags = dto.getTags();
        articleEntity.setTagId(tags.stream().map(Object::toString).collect(Collectors.joining(",")));
        articleEntity.setClassification(dto.getCategory());
        articleEntity.setUpdateTime(new Date());
        ArticleTextEntity articleTextEntity = new ArticleTextEntity();
        String prefix = SysDictionaries.getValue(DictionariesKey.SYS_MINIO_SITE);
        String zwf = SysDictionaries.getValue(DictionariesKey.IMG_ZWF);
        articleTextEntity.setArticleFullText(dto.getAllContent().replace(prefix,zwf));

        articleTextEntity.setUpdateTime(new Date());
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        boolean flag = false;
        try {
            this.baseMapper.update(articleEntity, new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, dto.getId()).eq(ArticleEntity::getTenantId, user.getId()));
            articleTextService.update(articleTextEntity, new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getArticleId, dto.getId()).eq(ArticleTextEntity::getTenantId, user.getId()));
            transactionManager.commit(status);
        }catch (Exception e){
            transactionManager.rollback(status);
           flag = true;
        }
        if (flag) {
            return R.error("网络错误");
        }
        return R.success();
    }

    @Override
    public R<Void> modifyCover(Long articleId, MultipartFile file) {
        String url = fileService.uploadSingle(file, FileConstant.builderCoverDir(AuthHandlerInterceptor.authLocal.get().getId()));
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setArticleImages(url);
        this.baseMapper.update(articleEntity, new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getId, articleId).eq(ArticleEntity::getTenantId, AuthHandlerInterceptor.authLocal.get().getId()));
        return R.success();
    }

    @Override
    public R<List<ArticleTitleAndIdVO>> getAllArticleTitleAndId() {
        Long userId = AuthHandlerInterceptor.authLocal.get().getId();
        List<ArticleEntity> articleEntities = this.baseMapper.selectList(new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getTenantId, userId).select(ArticleEntity::getTitle, ArticleEntity::getId));
        if (CollectionUtils.isEmpty(articleEntities)) {
            return R.success(new ArrayList<>());
        }
        List<ArticleTitleAndIdVO> result = articleEntities.stream().map(item -> {
            ArticleTitleAndIdVO vo = new ArticleTitleAndIdVO();
            BeanUtils.copyProperties(item, vo);
            return vo;
        }).collect(Collectors.toList());
        return R.success(result);
    }

    @Override
    public R<Void> comment(CommentDTO dto) {
        UserEntity userEntity = AuthHandlerInterceptor.authLocal.get();
        ArticleComment articleComment = new ArticleComment();
        articleComment.setArticleId(dto.getArticleId());
        articleComment.setCommentContent(dto.getContent());
        articleComment.setParentId(dto.getParentId());
        articleComment.setEmail(userEntity.getUsername());

        if(!dto.getParentId().equals(0L) && Objects.nonNull(dto.getToEmail())){
            //TODO 回复邮件通知
        }
        articleComment.setName(dto.getName());

        articleComment.setCommentContentDesensitization(dto.getContent());
        String ip = RequestUtil.getIp();
        articleComment.setIp(ip);
        articleComment.setIpAddress(RequestUtil.getPlace(ip));
        articleComment.setAiDistinguishing(CommentEnum.NO_AI_DISTINGUISHING.getCode());
        articleComment.setIsAuthor(1);
        articleComment.setToName(dto.getToName());
        this.commentMapper.insert(articleComment);
        SpringUtil.publishEvent(new ClearWebArticleDetailEvent(this, dto.getArticleId()));
        return R.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> deleteComment(Long id,Long articleId) {
        ArticleComment articleComment = this.commentMapper.selectOne(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getId, id).eq(ArticleComment::getArticleId, articleId));
        if (articleComment == null) {
            return R.error("评论不存在");
        }
        if(articleComment.getParentId().equals(0L)){
            //子评论删除
           this.commentMapper.delete(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getParentId, id).eq(ArticleComment::getArticleId, articleId));
        }
        this.commentMapper.delete(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getId, id).eq(ArticleComment::getArticleId, articleId));
        SpringUtil.publishEvent(new ClearWebArticleDetailEvent(this, articleComment.getArticleId()));
        return R.success();
    }


}




